﻿using Common.Job;
using Common.Model;
using Quartz;
using Quartz.Impl;
using Serilog;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Common.Communications
{
    /// <summary>
    /// Tcp通信服务
    /// </summary>
    public class TcpCommunication
    {
        public event Func<TcpReciveModel, Task> ReciveChange;

        public TcpClient TcpClient { get; private set; }

        protected Encoding Encoding { get; private set; } = Encoding.UTF8;

        private bool isConnect = false;
        private bool isClose = false;

        private string ip;
        private int iport;
        private byte[] ReadBytes = new byte[1024];
        private string heartBeatMsg = ConstString.HeartBeatMsg + ConstString.Termination;

        public TcpCommunication() { }

        // 连接服务器  
        public bool Connect(string host, int port)
        {
            if (isConnect) return true;
            try
            {
                ip = host;
                iport = port;
                if (TcpClient is not null)
                {
                    TcpClient.Close();
                }
                TcpClient = new TcpClient();
                TcpClient.BeginConnect(host, port, ConnectChecker, null);
                Log.Information($"{host}-连接成功");
                isConnect = true;
                //启动心跳
                SendHeartbeatAsync();
                return true;
            }
            catch (Exception ex) 
            {
                Log.Error($"{host}-连接失败-{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// 发送心跳
        /// </summary>
        /// <returns></returns>
        public async Task SendHeartbeatAsync()
        {
            var schedulerFactory = new StdSchedulerFactory();
            var scheduler = await schedulerFactory.GetScheduler();
            
            var jobDetail = JobBuilder.Create<OpticalJob>().Build();
            jobDetail.JobDataMap.Put("sendHeart", SendHeartbeat);
            var trigger = TriggerBuilder.Create()
                                        .WithSimpleSchedule(m => {
                                            m.WithIntervalInSeconds(20).RepeatForever();
                                        })
                                        .StartAt(DateTimeOffset.Now.AddSeconds(5))
                                        .Build();
            
            //添加心跳任务
            await scheduler.ScheduleJob(jobDetail, trigger);
            await scheduler.Start();
        }

        /// <summary>
        /// 发送心跳包
        /// </summary>
        public async Task SendHeartbeat()
        {
            try
            {
                if (!isConnect)
                {
                    return;
                }
                await SendAsync(heartBeatMsg);
            }
            catch (Exception ex)
            {
                Log.Error($"心跳发送异常{ex.Message}");
                return;
            }
        }

        public async Task<bool> SendAsync(string message)
        {
            try
            {
                if (TcpClient is null) return false;
                byte[] data = Encoding.GetBytes(message);
                //TcpClient.GetStream().BeginWrite(data, 0, data.Length, (ar) =>
                //{
                //    TcpClient.GetStream().EndWrite(ar);//结束异步发送
                //}, null);
                await TcpClient.GetStream().WriteAsync(data, 0, data.Length);
                return true;
            }
            catch (IOException ex)
            {
                Log.Error($"IO异常{ex.Message}");
                Connect(ip, iport);
                return false;
            }
            catch (Exception ex)
            {
                Log.Error($"发送异常{ex.Message}");
                Connect(ip, iport);
                return false;
            }
        }

        // 接收消息  
        private void ReceiveProcess(IAsyncResult ar)
        {
            try
            {
                if (TcpClient is null) return;
                int len = TcpClient.GetStream().EndRead(ar);
                if (len > 0)
                {
                    TcpReciveModel model = new TcpReciveModel(ip, Encoding.UTF8.GetString(ReadBytes));
                    ReciveChange?.Invoke(model);
                    ReadBytes = new byte[1024];
                    TcpClient.GetStream().BeginRead(ReadBytes, 0, ReadBytes.Length, ReceiveProcess, null);
                }
                else
                {
                    Connect(ip,iport);
                }
            }
            catch(IOException ex)
            {
                Log.Error($"IO异常{ex.Message}");
                Connect(ip, iport);
            }
            catch (Exception ex)
            {
                Log.Error($"接收异常{ex.Message}");
                Connect(ip, iport);
            }
        }

        // 关闭连接  
        public void CloseCon()
        {
            try 
            {
                if (!isConnect) return;

                if (TcpClient != null)
                {
                    TcpClient.Dispose();
                    TcpClient.Close();
                    TcpClient = null;
                    isConnect = false;
                }
                
                Log.Information($"连接关闭");
            }
            catch (Exception ex) 
            {
                Log.Error($"关闭异常{ex.Message}");
            }
        }
        /// <summary>
        /// 连接判断
        /// </summary>
        /// <returns></returns>
        private void ConnectChecker(IAsyncResult ar)
        {
            if (isClose)
            {
                return;
            }
            if (TcpClient.Connected == false)
            {
                TcpClient.Close();
                TcpClient = new TcpClient();
                TcpClient.BeginConnect(ip, iport, ConnectChecker, null);
            }
            else 
            {
                isConnect = false;
                TcpClient.EndConnect(ar);
                //读取数据
                TcpClient.GetStream().BeginRead(ReadBytes, 0, ReadBytes.Length, ReceiveProcess, null);
            }
        }

    }
}
